#include <linux/linkage.h>

#include <asm/assembler.h>
#include <asm/ptrace.h>

#define CONFIG_PREEMPT
#define CONFIG_GENERIC_IRQ_MULTI_HANDLER

#include "entry-header.S"

/*
 * Register switch for ARMv3 and ARMv4 processors
 * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info
 * previous and next are guaranteed not to be the same.
 */
ENTRY(__switch_to)
	@ .fnstart
	@ .cantunwind
	add	ip, r1, #TI_CPU_SAVE
	stmia	ip!, {r4 - sl, fp, sp, lr}	@ Store most regs on stack
	@ ldr	r4, [r2, #TI_TP_VALUE]
	@ ldr	r5, [r2, #TI_TP_VALUE + 4]
	@ switch_tls r1, r4, r5, r3, r7
	mov	r5, r0
	add	r4, r2, #TI_CPU_SAVE
	@ ldr	r0, =thread_notify_head
	@ mov	r1, #THREAD_NOTIFY_SWITCH
	@ bl	atomic_notifier_call_chain
	mov	r0, r5
	ldmia	r4, {r4 - sl, fp, sp, pc}	@ Load all regs saved previously
	@ .fnend
ENDPROC(__switch_to)

/*
 * Interrupt handling.
 */
	.macro	irq_handler
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
	ldr	r1, =handle_arch_irq
	mov	r0, sp
	adr	lr, 9997f
	ldr	pc, [r1]
#else
	arch_irq_handler_default
#endif
9997:
	.endm

#ifdef CONFIG_KPROBES
	.section	.kprobes.text,"ax",%progbits
#else
	.text
#endif

/*
 * Invalid mode handlers
 */
	.macro	inv_entry, reason
	sub	sp, sp, #S_FRAME_SIZE
	stmib	sp, {r1 - lr}
	mov	r1, #\reason
	.endm

__pabt_invalid:
	inv_entry BAD_PREFETCH
	b	common_invalid
ENDPROC(__pabt_invalid)

__dabt_invalid:
	inv_entry BAD_DATA
	b	common_invalid
ENDPROC(__dabt_invalid)

__irq_invalid:
	inv_entry BAD_IRQ
	b	common_invalid
ENDPROC(__irq_invalid)

__und_invalid:
	inv_entry BAD_UNDEFINSTR

	@
	@ XXX fall through to common_invalid
	@

@
@ common_invalid - generic code for failed exception (re-entrant version of handlers)
@
common_invalid:
	zero_fp

	ldmia	r0, {r4 - r6}
	add	r0, sp, #S_PC		@ here for interlock avoidance
	mov	r7, #-1			@  ""   ""    ""        ""
	str	r4, [sp]		@ save preserved r0
	stmia	r0, {r5 - r7}		@ lr_<exception>,
					@ cpsr_<exception>, "old_r0"

	mov	r0, sp
	@ b	bad_mode
ENDPROC(__und_invalid)

/*
 * SVC mode handlers
 */

@ #if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5)
@ #define SPFIX(code...) code
@ #else
#define SPFIX(code...)
@ #endif

	.macro	svc_entry, stack_hole=0
	sub	sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
 SPFIX(	tst	sp, #4		)
 SPFIX(	subeq	sp, sp, #4	)
	stmia	sp, {r1 - r12}

	ldmia	r0, {r1 - r3}
	add	r5, sp, #S_SP - 4	@ here for interlock avoidance
	mov	r4, #-1			@  ""  ""      ""       ""
	add	r0, sp, #(S_FRAME_SIZE + \stack_hole - 4)
 SPFIX(	addeq	r0, r0, #4	)
	str	r1, [sp, #-4]!		@ save the "real" r0 copied
					@ from the exception stack

	mov	r1, lr

	@
	@ We are now ready to fill in the remaining blanks on the stack:
	@
	@  r0 - sp_svc
	@  r1 - lr_svc
	@  r2 - lr_<exception>, already fixed up for correct return/restart
	@  r3 - spsr_<exception>
	@  r4 - orig_r0 (see pt_regs definition in ptrace.h)
	@
	stmia	r5, {r0 - r4}

	@ asm_trace_hardirqs_off
	.endm

	.align	5
__dabt_svc:
	svc_entry

	@
	@ get ready to re-enable interrupts if appropriate
	@
	mrs	r9, cpsr
	tst	r3, #PSR_I_BIT
	biceq	r9, r9, #PSR_I_BIT

	@
	@ Call the processor-specific abort handler:
	@
	@  r2 - aborted context pc
	@  r3 - aborted context cpsr
	@
	@ The abort handler must return the aborted address in r0, and
	@ the fault status register in r1.  r9 must be preserved.
	@
#ifdef MULTI_DABORT
	ldr	r4, .LCprocfns
	mov	lr, pc
	ldr	pc, [r4, #PROCESSOR_DABT_FUNC]
#else
	@ bl	CPU_DABORT_HANDLER
	bl v7_early_abort
#endif

	@
	@ set desired IRQ state, then call main handler
	@
	msr	cpsr_c, r9
	mov	r2, sp
	bl	do_DataAbort

	@
	@ IRQs off again before pulling preserved data off the stack
	@
	disable_irq

	@
	@ restore SPSR and restart the instruction
	@
	ldr	r2, [sp, #S_PSR]
	svc_exit r2				@ return from exception
ENDPROC(__dabt_svc)

	.align	5
__irq_svc:
	svc_entry

#ifdef CONFIG_PREEMPT
	get_thread_info tsk
	ldr	r8, [tsk, #TI_PREEMPT]		@ get preempt count
	add	r7, r8, #1			@ increment it
	str	r7, [tsk, #TI_PREEMPT]
#endif

	irq_handler
#ifdef CONFIG_PREEMPT
	str	r8, [tsk, #TI_PREEMPT]		@ restore preempt count
	ldr	r0, [tsk, #TI_FLAGS]		@ get flags
	teq	r8, #0				@ if preempt count != 0
	movne	r0, #0				@ force flags to 0
	tst	r0, #_TIF_NEED_RESCHED
	blne	svc_preempt
#endif
	ldr	r4, [sp, #S_PSR]		@ irqs are already disabled
#ifdef CONFIG_TRACE_IRQFLAGS
	tst	r4, #PSR_I_BIT
	bleq	trace_hardirqs_on
#endif
	svc_exit r4				@ return from exception
ENDPROC(__irq_svc)

	.ltorg

#ifdef CONFIG_PREEMPT
svc_preempt:
	mov	r8, lr
1:	bl	preempt_schedule_irq		@ irq en/disable is done inside
	ldr	r0, [tsk, #TI_FLAGS]		@ get new tasks TI_FLAGS
	tst	r0, #_TIF_NEED_RESCHED
	moveq	pc, r8				@ go again
	b	1b
#endif

	.align	5
__und_svc:
@ #ifdef CONFIG_KPROBES
@ 	@ If a kprobe is about to simulate a "stmdb sp..." instruction,
@ 	@ it obviously needs free stack space which then will belong to
@ 	@ the saved context.
@ 	svc_entry 64
@ #else
@ 	svc_entry
@ #endif

@ 	@
@ 	@ call emulation code, which returns using r9 if it has emulated
@ 	@ the instruction, or the more conventional lr if we are to treat
@ 	@ this as a real undefined instruction
@ 	@
@ 	@  r0 - instruction
@ 	@
@ #ifndef	CONFIG_THUMB2_KERNEL
@ 	ldr	r0, [r2, #-4]
@ #else
@ 	ldrh	r0, [r2, #-2]			@ Thumb instruction at LR - 2
@ 	and	r9, r0, #0xf800
@ 	cmp	r9, #0xe800			@ 32-bit instruction if xx >= 0
@ 	ldrhhs	r9, [r2]			@ bottom 16 bits
@ 	orrhs	r0, r9, r0, lsl #16
@ #endif
@ 	adr	r9, BSYM(1f)
@ 	bl	call_fpe

@ 	mov	r0, sp				@ struct pt_regs *regs
@ 	bl	do_undefinstr

@ 	@
@ 	@ IRQs off again before pulling preserved data off the stack
@ 	@
@ 1:	disable_irq

@ 	@
@ 	@ restore SPSR and restart the instruction
@ 	@
@ 	ldr	r2, [sp, #S_PSR]		@ Get SVC cpsr
@ 	svc_exit r2				@ return from exception
@  UNWIND(.fnend		)
ENDPROC(__und_svc)

	.align	5
__pabt_svc:
	svc_entry

	@
	@ re-enable interrupts if appropriate
	@
	mrs	r9, cpsr
	tst	r3, #PSR_I_BIT
	biceq	r9, r9, #PSR_I_BIT

	mov	r0, r2			@ pass address of aborted instruction.
@ #ifdef MULTI_PABORT
@ 	ldr	r4, .LCprocfns
@ 	mov	lr, pc
@ 	ldr	pc, [r4, #PROCESSOR_PABT_FUNC]
@ #else
	@ bl	CPU_PABORT_HANDLER
	bl v7_pabort
@ #endif
	msr	cpsr_c, r9			@ Maybe enable interrupts
	mov	r2, sp				@ regs
	bl	do_PrefetchAbort		@ call abort handler

	@
	@ IRQs off again before pulling preserved data off the stack
	@
	disable_irq

	@
	@ restore SPSR and restart the instruction
	@
	ldr	r2, [sp, #S_PSR]
	svc_exit r2				@ return from exception
@  UNWIND(.fnend		)
ENDPROC(__pabt_svc)

	.align	5
.LCcralign:
	.word	cr_alignment
#ifdef MULTI_DABORT
.LCprocfns:
	.word	processor
#endif
@ .LCfp:
@ 	.word	fp_enter

/*
 * User mode handlers
 *
 * EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
 */

#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
#error "sizeof(struct pt_regs) must be a multiple of 8"
#endif

	.macro	usr_entry
	sub	sp, sp, #S_FRAME_SIZE
	stmib	sp, {r1 - r12}

	ldmia	r0, {r1 - r3}
	add	r0, sp, #S_PC		@ here for interlock avoidance
	mov	r4, #-1			@  ""  ""     ""        ""

	str	r1, [sp]		@ save the "real" r0 copied
					@ from the exception stack

	@
	@ We are now ready to fill in the remaining blanks on the stack:
	@
	@  r2 - lr_<exception>, already fixed up for correct return/restart
	@  r3 - spsr_<exception>
	@  r4 - orig_r0 (see pt_regs definition in ptrace.h)
	@
	@ Also, separately save sp_usr and lr_usr
	@
	stmia	r0, {r2 - r4}
	stmdb	r0, {sp, lr}^

	@
	@ Enable the alignment trap while in kernel mode
	@
	alignment_trap r0

	@
	@ Clear FP to mark the first stack frame
	@
	zero_fp

	@ asm_trace_hardirqs_off
	.endm

	.align	5
__dabt_usr:
	usr_entry
	@ kuser_cmpxchg_check

	@
	@ Call the processor-specific abort handler:
	@
	@  r2 - aborted context pc
	@  r3 - aborted context cpsr
	@
	@ The abort handler must return the aborted address in r0, and
	@ the fault status register in r1.
	@
#ifdef MULTI_DABORT
	ldr	r4, .LCprocfns
	mov	lr, pc
	ldr	pc, [r4, #PROCESSOR_DABT_FUNC]
#else
	@ bl	CPU_DABORT_HANDLER
	bl v7_early_abort
#endif

	@
	@ IRQs on, then call the main handler
	@
	enable_irq
	mov	r2, sp
	adr	lr, ret_from_exception
	b	do_DataAbort
ENDPROC(__dabt_usr)

	.align	5
__irq_usr:
	usr_entry

	get_thread_info tsk
#ifdef CONFIG_PREEMPT
	ldr	r8, [tsk, #TI_PREEMPT]		@ get preempt count
	add	r7, r8, #1			@ increment it
	str	r7, [tsk, #TI_PREEMPT]
#endif

	irq_handler
#ifdef CONFIG_PREEMPT
	ldr	r0, [tsk, #TI_PREEMPT]
	str	r8, [tsk, #TI_PREEMPT]
	teq	r0, r7
	strne	r0, [r0, -r0]
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
	bl	trace_hardirqs_on
#endif

	mov	why, #0
	b	ret_to_user
ENDPROC(__irq_usr)

	.ltorg

	.align	5
__und_usr:
@ 	usr_entry

@ 	@
@ 	@ fall through to the emulation code, which returns using r9 if
@ 	@ it has emulated the instruction, or the more conventional lr
@ 	@ if we are to treat this as a real undefined instruction
@ 	@
@ 	@  r0 - instruction
@ 	@
@ 	adr	r9, BSYM(ret_from_exception)
@ 	adr	lr, BSYM(__und_usr_unknown)
@ 	tst	r3, #PSR_T_BIT			@ Thumb mode?
@ 	itet	eq				@ explicit IT needed for the 1f label
@ 	subeq	r4, r2, #4			@ ARM instr at LR - 4
@ 	subne	r4, r2, #2			@ Thumb instr at LR - 2
@ 1:	ldreqt	r0, [r4]
@ #ifdef CONFIG_CPU_ENDIAN_BE8
@ 	reveq	r0, r0				@ little endian instruction
@ #endif
@ 	beq	call_fpe
@ 	@ Thumb instruction
@ #if __LINUX_ARM_ARCH__ >= 7
@ 2:
@  ARM(	ldrht	r5, [r4], #2	)
@  THUMB(	ldrht	r5, [r4]	)
@  THUMB(	add	r4, r4, #2	)
@ 	and	r0, r5, #0xf800			@ mask bits 111x x... .... ....
@ 	cmp	r0, #0xe800			@ 32bit instruction if xx != 0
@ 	blo	__und_usr_unknown
@ 3:	ldrht	r0, [r4]
@ 	add	r2, r2, #2			@ r2 is PC + 2, make it PC + 4
@ 	orr	r0, r0, r5, lsl #16
@ #else
@ 	b	__und_usr_unknown
@ #endif
@  UNWIND(.fnend		)
ENDPROC(__und_usr)

	.align	5
__pabt_usr:
	usr_entry

	mov	r0, r2			@ pass address of aborted instruction.
@ #ifdef MULTI_PABORT
@ 	ldr	r4, .LCprocfns
@ 	mov	lr, pc
@ 	ldr	pc, [r4, #PROCESSOR_PABT_FUNC]
@ #else
	@ bl	CPU_PABORT_HANDLER
	bl v7_pabort
@ #endif
	enable_irq				@ Enable interrupts
	mov	r2, sp				@ regs
	bl	do_PrefetchAbort		@ call abort handler
@  UNWIND(.fnend		)
	/* fall through */
/*
 * This is the return code to user mode for abort handlers
 */
ENTRY(ret_from_exception)
	get_thread_info tsk
	mov	why, #0
	b	ret_to_user
ENDPROC(__pabt_usr)
ENDPROC(ret_from_exception)

/*
 * Vector stubs.
 *
 * This code is copied to 0xffff0200 so we can use branches in the
 * vectors, rather than ldr's.  Note that this code must not
 * exceed 0x300 bytes.
 *
 * Common stub entry macro:
 *   Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 *
 * SP points to a minimal amount of processor-private memory, the address
 * of which is copied into r0 for the mode specific abort handler.
 */
	.macro	vector_stub, name, mode, correction=0
	.align	5

vector_\name:
	.if \correction
	sub	lr, lr, #\correction
	.endif

	@
	@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
	@ (parent CPSR)
	@
	stmia	sp, {r0, lr}		@ save r0, lr
	mrs	lr, spsr
	str	lr, [sp, #8]		@ save spsr

	@
	@ Prepare for SVC32 mode.  IRQs remain disabled.
	@
	mrs	r0, cpsr
	eor	r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
	msr	spsr_cxsf, r0

	@
	@ the branch table must immediately follow this code
	@
	and	lr, lr, #0x0f
	mov	r0, sp
	ldr	lr, [pc, lr, lsl #2]
	movs	pc, lr			@ branch to handler in SVC mode
ENDPROC(vector_\name)

	.align	2
	@ handler addresses follow this label
1:
	.endm

	.globl	__stubs_start
__stubs_start:
/*
 * Interrupt dispatcher
 */
	vector_stub	irq, IRQ_MODE, 4

	.long	__irq_usr			@  0  (USR_26 / USR_32)
	.long	__irq_invalid			@  1  (FIQ_26 / FIQ_32)
	.long	__irq_invalid			@  2  (IRQ_26 / IRQ_32)
	.long	__irq_svc			@  3  (SVC_26 / SVC_32)
	.long	__irq_invalid			@  4
	.long	__irq_invalid			@  5
	.long	__irq_invalid			@  6
	.long	__irq_invalid			@  7
	.long	__irq_invalid			@  8
	.long	__irq_invalid			@  9
	.long	__irq_invalid			@  a
	.long	__irq_invalid			@  b
	.long	__irq_invalid			@  c
	.long	__irq_invalid			@  d
	.long	__irq_invalid			@  e
	.long	__irq_invalid			@  f

/*
 * Data abort dispatcher
 * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
 */
	vector_stub	dabt, ABT_MODE, 8

	.long	__dabt_usr			@  0  (USR_26 / USR_32)
	.long	__dabt_invalid			@  1  (FIQ_26 / FIQ_32)
	.long	__dabt_invalid			@  2  (IRQ_26 / IRQ_32)
	.long	__dabt_svc			@  3  (SVC_26 / SVC_32)
	.long	__dabt_invalid			@  4
	.long	__dabt_invalid			@  5
	.long	__dabt_invalid			@  6
	.long	__dabt_invalid			@  7
	.long	__dabt_invalid			@  8
	.long	__dabt_invalid			@  9
	.long	__dabt_invalid			@  a
	.long	__dabt_invalid			@  b
	.long	__dabt_invalid			@  c
	.long	__dabt_invalid			@  d
	.long	__dabt_invalid			@  e
	.long	__dabt_invalid			@  f

/*
 * Prefetch abort dispatcher
 * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
 */
	vector_stub	pabt, ABT_MODE, 4

	.long	__pabt_usr			@  0 (USR_26 / USR_32)
	.long	__pabt_invalid			@  1 (FIQ_26 / FIQ_32)
	.long	__pabt_invalid			@  2 (IRQ_26 / IRQ_32)
	.long	__pabt_svc			@  3 (SVC_26 / SVC_32)
	.long	__pabt_invalid			@  4
	.long	__pabt_invalid			@  5
	.long	__pabt_invalid			@  6
	.long	__pabt_invalid			@  7
	.long	__pabt_invalid			@  8
	.long	__pabt_invalid			@  9
	.long	__pabt_invalid			@  a
	.long	__pabt_invalid			@  b
	.long	__pabt_invalid			@  c
	.long	__pabt_invalid			@  d
	.long	__pabt_invalid			@  e
	.long	__pabt_invalid			@  f

/*
 * Undef instr entry dispatcher
 * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 */
	vector_stub	und, UND_MODE

	.long	__und_usr			@  0 (USR_26 / USR_32)
	.long	__und_invalid			@  1 (FIQ_26 / FIQ_32)
	.long	__und_invalid			@  2 (IRQ_26 / IRQ_32)
	.long	__und_svc			@  3 (SVC_26 / SVC_32)
	.long	__und_invalid			@  4
	.long	__und_invalid			@  5
	.long	__und_invalid			@  6
	.long	__und_invalid			@  7
	.long	__und_invalid			@  8
	.long	__und_invalid			@  9
	.long	__und_invalid			@  a
	.long	__und_invalid			@  b
	.long	__und_invalid			@  c
	.long	__und_invalid			@  d
	.long	__und_invalid			@  e
	.long	__und_invalid			@  f

	.align	5

/*=============================================================================
 * Undefined FIQs
 *-----------------------------------------------------------------------------
 * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
 * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
 * Basically to switch modes, we *HAVE* to clobber one register...  brain
 * damage alert!  I don't think that we can execute any code in here in any
 * other mode than FIQ...  Ok you can switch to another mode, but you can't
 * get out of that mode without clobbering one register.
 */
vector_fiq:
	@ disable_fiq
	subs	pc, lr, #4

/*=============================================================================
 * Address exception handler
 *-----------------------------------------------------------------------------
 * These aren't too critical.
 * (they're not supposed to happen, and won't happen in 32-bit data mode).
 */

vector_addrexcptn:
	b	vector_addrexcptn

/*
 * We group all the following data together to optimise
 * for CPUs with separate I & D caches.
 */
	.align	5

.LCvswi:
	.word	vector_swi

.LCvled:
	.word	led_on

@ .LCvint:
@ 	.word	irq_handler

	.globl	__stubs_end
__stubs_end:

	.equ	stubs_offset, __vectors_start + 0x200 - __stubs_start

	.globl	__vectors_start
__vectors_start:
	swi	SYS_ERROR0
	b	vector_und + stubs_offset
	ldr	pc, .LCvswi + stubs_offset
	@ ldr	pc, .LCvled + stubs_offset
	b	vector_pabt + stubs_offset
	b	vector_dabt + stubs_offset
	b	vector_addrexcptn + stubs_offset
	b	vector_irq + stubs_offset
	@ ldr	pc, .LCvled + stubs_offset
	@ ldr	pc, .LCvint + stubs_offset
	b	vector_fiq + stubs_offset

	.globl	__vectors_end
__vectors_end:
